#include "mainwindow.h"
#include <QFileDialog>
#include <QSerialPort>
#include <QSerialPortInfo>
#include <QFile>
#include <QByteArray>
#include <QMessageBox>
#include <QTimer>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QDateTime>
#include <QSpacerItem>

MainWindow::MainWindow(QWidget *parent) :
    QWidget(parent)
{
    // ui->setupUi(this);

    m_serialPort = new QSerialPort(this);
    m_respondTimer = new QTimer(this);
    m_firmwareUpgrader = new FirmwareUpgrader(this);

    m_respondTimer->setInterval(500);

    // Initialize UI elements
    QLabel *label_serialPort = new QLabel("Serial Port:", this);
    QLabel *label_baudrate = new QLabel("Baudrate:", this);
    QLabel *label_filePath = new QLabel("File Path:", this);
    QLabel *label_progress = new QLabel("Progress:", this);
    QLabel *label_deviceAddress = new QLabel("Device Address:", this); // New label for device address
    QLabel *label_crc = new QLabel("CRC:", this); // New label for CRC
    label_file_size = new QLabel("File Size: 0 bytes", this);
    label_connection_status = new QLabel(this);
    label_connection_status->setFixedSize(20, 20);
    label_connection_status->setStyleSheet("background-color: red; border-radius: 10px;");
    textEdit_log = new QTextEdit(this);
    textEdit_log->setReadOnly(true);

    pushButton_open_bin = new QPushButton("Open Bin File", this);
    pushButton_down_fw = new QPushButton("Download Firmware", this);
    pushButton_refresh_sp = new QPushButton("Refresh", this);
    pushButton_open_sp = new QPushButton("Open Serial Port", this);
    pushButton_clear_log = new QPushButton("Clear Log", this);
    pushButton_soft_reset = new QPushButton("MCU Soft Reset", this); // New button for MCU soft reset
    pushButton_upgrade_reset = new QPushButton("Upgrade State Machine Reset", this); // New button for upgrade state machine reset
    // Set button styles
    QString buttonStyle = "QPushButton {"
                "border-radius: 10px;"
                "background-color:rgb(20, 66, 104);"
                "color: white;"
                "padding: 5px 10px;"

                "}";

    pushButton_open_bin->setStyleSheet(buttonStyle);
    pushButton_down_fw->setStyleSheet(buttonStyle);
    pushButton_refresh_sp->setStyleSheet(buttonStyle);
    pushButton_open_sp->setStyleSheet(buttonStyle);
    pushButton_clear_log->setStyleSheet(buttonStyle);
    pushButton_soft_reset->setStyleSheet(buttonStyle);
    pushButton_upgrade_reset->setStyleSheet(buttonStyle);

    comboBox_baudrate = new QComboBox(this);
    comboBox_sp = new QComboBox(this);
    lineEdit_file_path = new QLineEdit(this);
    lineEdit_device_address = new QLineEdit("0", this); // New line edit for device address
    lineEdit_crc = new QLineEdit(this); // New line edit for CRC
    lineEdit_crc->setReadOnly(true); // Make CRC field read-only
    progressBar_down = new QProgressBar(this);

    progressBar_down->setRange(0, 100);
    progressBar_down->setValue(0);
    progressBar_down->setStyleSheet(
      "QProgressBar { "
      "border-radius: 5px; "
      "text-align: center; "
      "} "
      "QProgressBar::chunk { "
      "background-color: green; "
      "border-radius: 5px; "
      "}"
    );
    comboBox_baudrate->addItems({"9600", "19200", "38400", "57600", "115200"});
    comboBox_baudrate->setCurrentIndex(4);
    onRefreshSerialPort();
    m_isDowning = false;
    m_binFilePackIndex = 0;

    label_downloaded_bytes = new QLabel("Downloaded: 0 / 0 bytes", this);
    checkBox_ackRequired = new QCheckBox("Require Acknowledgment", this);
    checkBox_ackRequired->setChecked(true); // Default to requiring acknowledgment

    // Layout setup
    QHBoxLayout *mainLayout = new QHBoxLayout(this);

    // Serial port section
    QVBoxLayout *serialPortLayout = new QVBoxLayout();
    QHBoxLayout *serialPortSelectLayout = new QHBoxLayout();
    QHBoxLayout *baudrateSelectLayout = new QHBoxLayout();
    QHBoxLayout *serialPortOpenLayout = new QHBoxLayout();

    serialPortSelectLayout->addWidget(label_serialPort);
    serialPortSelectLayout->addWidget(comboBox_sp);
    serialPortSelectLayout->addWidget(pushButton_refresh_sp);

    baudrateSelectLayout->addWidget(label_baudrate);
    baudrateSelectLayout->addWidget(comboBox_baudrate);

    serialPortOpenLayout->addWidget(label_connection_status);
    serialPortOpenLayout->addWidget(pushButton_open_sp);

    serialPortLayout->addLayout(serialPortSelectLayout);
    serialPortLayout->addLayout(baudrateSelectLayout);
    serialPortLayout->addLayout(serialPortOpenLayout);
    serialPortLayout->addSpacerItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding));

    // Firmware section
    QVBoxLayout *firmwareLayout = new QVBoxLayout();
    QHBoxLayout *fileLayout = new QHBoxLayout();
    QHBoxLayout *fileInfoLayout = new QHBoxLayout();
    QHBoxLayout *operatorLayout = new QHBoxLayout();



    fileLayout->addWidget(label_filePath);
    fileLayout->addWidget(lineEdit_file_path);
    fileLayout->addWidget(pushButton_open_bin);
    firmwareLayout->addLayout(fileLayout);

    fileInfoLayout->addWidget(label_file_size);
    fileInfoLayout->addSpacerItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
    fileInfoLayout->addWidget(label_crc);
    fileInfoLayout->addWidget(lineEdit_crc);
    fileInfoLayout->addSpacerItem(new QSpacerItem(20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
    firmwareLayout->addLayout(fileInfoLayout);

    operatorLayout->addWidget(label_deviceAddress);
    operatorLayout->addWidget(lineEdit_device_address);
    operatorLayout->addWidget(checkBox_ackRequired);
    operatorLayout->addWidget(pushButton_down_fw);
    operatorLayout->addWidget(pushButton_soft_reset);
    operatorLayout->addWidget(pushButton_upgrade_reset);
    firmwareLayout->addLayout(operatorLayout);

    //firmwareLayout->addWidget(label_file_size);
    //firmwareLayout->addWidget(label_crc); // Add CRC label to layout
    //firmwareLayout->addWidget(lineEdit_crc); // Add CRC input to layout
    //firmwareLayout->addWidget(pushButton_down_fw);
    firmwareLayout->addWidget(label_progress);
    firmwareLayout->addWidget(progressBar_down);
    firmwareLayout->addWidget(label_downloaded_bytes);
    //firmwareLayout->addWidget(checkBox_ackRequired); // Add the checkbox to the layout
    //firmwareLayout->addWidget(pushButton_soft_reset); // Add the soft reset button to the layout
    //firmwareLayout->addWidget(pushButton_upgrade_reset); // Add the upgrade state machine reset button to the layout
    firmwareLayout->addWidget(textEdit_log);
    firmwareLayout->addWidget(pushButton_clear_log); // Add the clear log button to the layout

    // Add sections to main layout
    mainLayout->addLayout(serialPortLayout);
    mainLayout->addLayout(firmwareLayout);
    setLayout(mainLayout);

    connect(pushButton_open_bin, &QPushButton::clicked, this, &MainWindow::onOpenBinFile);
    connect(pushButton_down_fw, &QPushButton::clicked, this, &MainWindow::onDownloadBinFile);
    connect(pushButton_refresh_sp, &QPushButton::clicked, this, &MainWindow::onRefreshSerialPort);
    connect(pushButton_open_sp, &QPushButton::clicked, this, &MainWindow::onOpenSerialPort);
    connect(pushButton_clear_log, &QPushButton::clicked, this, &MainWindow::onClearLog); // Connect the clear log button to the slot
    connect(pushButton_soft_reset, &QPushButton::clicked, m_firmwareUpgrader, &FirmwareUpgrader::sendSoftResetCommand); // Connect the soft reset button to the method
    connect(pushButton_upgrade_reset, &QPushButton::clicked, m_firmwareUpgrader, &FirmwareUpgrader::sendUpgradeStateMachineResetCommand); // Connect the upgrade state machine reset button to the method
    connect(lineEdit_device_address, &QLineEdit::textChanged, this, &MainWindow::onDeviceAddressChanged); // Connect device address change to the slot
    connect(m_serialPort, &QSerialPort::readyRead, this, &MainWindow::onSerialportReadyRead);
    // connect(this->m_respondTimer, &QTimer::timeout, this, &MainWindow::onRespondTimeout);

    connect(m_firmwareUpgrader, &FirmwareUpgrader::progressUpdated, this, [this](int value) {
        progressBar_down->setValue(value);
        label_downloaded_bytes->setText(QString("Downloaded: %1%").arg(value));
    });
    connect(m_firmwareUpgrader, &FirmwareUpgrader::upgradeCompleted, this, [this]() {
        m_firmwareUpgrader->sendSoftResetCommand();
        QMessageBox::information(nullptr, "Upgrade", "Firmware upgrade completed successfully.");
        m_isDowning = false;
        pushButton_down_fw->setText(tr("download")); // Switch back to start button
        checkBox_ackRequired->setEnabled(true); // Enable the checkbox
    });
    connect(m_firmwareUpgrader, &FirmwareUpgrader::upgradeFailed, this, [this]() {
        QMessageBox::critical(nullptr, "Upgrade", "Firmware upgrade failed.");
        m_isDowning = false;
        pushButton_down_fw->setText(tr("download")); // Switch back to start button
        checkBox_ackRequired->setEnabled(true); // Enable the checkbox
    });
    connect(m_firmwareUpgrader, &FirmwareUpgrader::sendData, this, &MainWindow::writeToSerialPort);
    connect(checkBox_ackRequired, &QCheckBox::toggled, m_firmwareUpgrader, &FirmwareUpgrader::setAckRequired); // Connect checkbox to firmware upgrader
    connect(m_firmwareUpgrader, &FirmwareUpgrader::crcCalculated, this, [this](quint16 crc) {
        lineEdit_crc->setText(QString::number(crc, 16).toUpper().rightJustified(4, '0')); // Display CRC in uppercase hex
    });
}

MainWindow::~MainWindow()
{
    delete m_serialPort;
    delete m_respondTimer;
    delete pushButton_open_bin;
    delete pushButton_down_fw;
    delete pushButton_refresh_sp;
    delete pushButton_open_sp;
    delete pushButton_clear_log;
    delete comboBox_baudrate;
    delete comboBox_sp;
    delete lineEdit_file_path;
    delete progressBar_down;
    delete m_firmwareUpgrader;
    delete label_file_size;
    delete label_connection_status;
    delete textEdit_log;
    delete label_downloaded_bytes;
    delete checkBox_ackRequired;
    delete lineEdit_device_address; // Delete the new line edit for device address
    delete lineEdit_crc; // Delete the new line edit for CRC
}

void MainWindow::onOpenBinFile()
{
    m_binPath = QFileDialog::getOpenFileName(this, "Open File", "", "All Files (*.*)");
    if (!m_binPath.isEmpty())
    {
        lineEdit_file_path->setText(m_binPath);
        QFile file(m_binPath);
        if (file.open(QIODevice::ReadOnly))
        {
            qint64 fileSize = file.size();
            label_file_size->setText(QString("File Size: %1 bytes").arg(fileSize));
            file.close();
        }
    }
}

void MainWindow::onDownloadBinFile()
{
    if(m_isDowning)
    {
        m_isDowning = false;
        pushButton_down_fw->setText(tr("download"));
        progressBar_down->setValue(0);
        checkBox_ackRequired->setEnabled(true); // Enable the checkbox
        m_firmwareUpgrader->stopUpgrade(); // Stop the upgrade process

        return;
    }

    if(!m_serialPort->isOpen())
    {
        QMessageBox::critical(this, "Error", "open serial port first.");
        return;
    }

    bool ok;
    quint8 deviceAddress = lineEdit_device_address->text().toUInt(&ok, 16); // Get device address from input
    if (!ok) {
        QMessageBox::critical(this, "Error", "Invalid device address.");
        return;
    }

    m_isDowning = true;
    checkBox_ackRequired->setEnabled(false); // Disable the checkbox
    m_firmwareUpgrader->setAckRequired(checkBox_ackRequired->isChecked()); // Set acknowledgment requirement
    m_firmwareUpgrader->startUpgrade(m_binPath, deviceAddress); // Pass device address to startUpgrade
    pushButton_down_fw->setText(tr("stop"));
}

void MainWindow::onRefreshSerialPort()
{
    comboBox_sp->clear();
    const auto ports = QSerialPortInfo::availablePorts();

    for(const QSerialPortInfo &port : ports)
    {
        comboBox_sp->addItem(port.portName());
    }
}

void MainWindow::onOpenSerialPort()
{
    if (m_serialPort->isOpen())
    {
        m_serialPort->close();
        pushButton_open_sp->setText(tr("open"));
        pushButton_refresh_sp->setEnabled(true);
        label_connection_status->setStyleSheet("background-color: rgb(255, 0, 0); border-radius: 10px;");
        return;
    }

    m_serialPort->setPortName(comboBox_sp->currentText());
    m_serialPort->setBaudRate(comboBox_baudrate->currentText().toInt());
    m_serialPort->setDataBits(QSerialPort::Data8);
    m_serialPort->setParity(QSerialPort::NoParity);
    m_serialPort->setStopBits(QSerialPort::OneStop);
    m_serialPort->setFlowControl(QSerialPort::NoFlowControl);

    if (!m_serialPort->open(QIODevice::ReadWrite))
    {
        QMessageBox::critical(this, "Error", "Failed to open serial port.");
        return;
    }

    pushButton_open_sp->setText(tr("close"));
    pushButton_refresh_sp->setDisabled(true);
    label_connection_status->setStyleSheet("background-color: rgb(0, 255, 0); border-radius: 10px;");
}

void MainWindow::onSerialportReadyRead()
{
    QTimer::singleShot(50, this, [this]() {
        QByteArray responseData = m_serialPort->readAll();
        QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
        textEdit_log->append(QString("[%1] Received: %2").arg(timestamp).arg(QString(responseData.toHex(' ').toUpper())));
        m_firmwareUpgrader->parseSerialData(responseData);
    });
}

void MainWindow::onRespondTimeout()
{
    // Handle response timeout if needed
}

void MainWindow::writeToSerialPort(const QByteArray &data)
{
    if (m_serialPort->isOpen() && m_serialPort->isWritable())
    {
        m_serialPort->write(data);
        QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
        textEdit_log->append(QString("[%1] Sent: %2").arg(timestamp).arg(QString(data.toHex(' ').toUpper())));
    }
    else
    {
        QMessageBox::critical(this, "Error", "Serial port is not open or not writable.");
        m_firmwareUpgrader->upgradeFailed();
    }
}

void MainWindow::onClearLog()
{
    textEdit_log->clear();
}

void MainWindow::onDeviceAddressChanged(const QString &address)
{
    bool ok;
    quint8 deviceAddress = address.toUInt(&ok, 16); // Convert address to uint
    if (ok) {
        m_firmwareUpgrader->setDeviceAddress(deviceAddress); // Update device address in FirmwareUpgrader
    }
}
